Chris Pollett >Old Classes >
CS157b

( Print View )

Advertisement:
  [
CS185C PDA Course]

Student Corner:
  [Grades Sec2]
  [Grades Sec3]

  [Submit Sec2]
  [Submit Sec3]

  [Lecture Notes]

Course Info:
  [Texts & Links]
  [Topics]
  [Grading]
  [HW Info]
  [Exam Info]
  [Regrades]
  [Honesty]
  [Announcements]

HW Assignments:
  [Hw1]  [Hw2]  [Hw3]
  [Hw4]  [Hw5]

Practice Exams:
  [Mid1]  [Mid2]  [Final]

                           












HW#3 --- last modified March 02 2019 17:05:06..

Solution set.

Due date: Oct 27

Files to be submitted:
  HeapMockDisk.java
  LRUBufferManager.java
  TextbookProblems.pdf
  Tests.zip

Purpose: To gain experience with how heap files are implemented in DBMS's, to code the LRU buffer replacement policy, and to understand query evaluation and external sorting.

Specification:

First, do problems 12.5 and 13.3 and submit them in TextbookProblems.pdf.

The coding part of the assignment consists of taking the code below and modifying it so that it supports Linked-List of Pages Heap Files as describe on page 325 of the book and so that one can create BufferManager's that use the Least Recently Used page replacement policy.

To be more specific I want you to create subclasses HeapMockDisk and LRUBufferManager of the MockDisk and BufferManager classes below. To make HeapMockDisk I want you to extend MockDisk and add the following methods:

public void deleteFile(String fileName)
   throws MissingResourceException
/* this deletes a file from the directory and deallocates its blocks
   from the PAM.
   It throws a MissingResourceException if the filename is not
   found in the directory.
*/

public String toStringFile(String fileName)
   throws MissingResourceException
/*
   This returns a string containing each record in a file,
   one newline after each record. To draw a record it prints
   the bytes in the record separated by spaces. For example,
   suppose the file had record size of 3 byte and two records in it.
   a possible string returned might be:
   "255 0 148\n192 16 12"

   It throws a MissingResourceException if the filename is not
   found in the directory.
*/

public void addRecord(String fileName, byte[] record)
   throws MissingResourceException, IllegalArgumentException
/*
   Adds the given record to the given heap file
   Should use the available pages linked list as described in the book.
   If the page becomes full after the addition, page should be moved
   to the full pages linked list.

   It throws a MissingResourceException if the filename is not
   found in the directory.

   It throws an IllegalArgumentException if the record contains
   more bytes then the record size of the given heap file.

*/

public void deleteRecord(String fileName, byte[] record)
   throws MissingResourceException, IllegalArgumentException
/*
   Delete the given record from the given heap file
   Should check both the available pages linked list
   and the full pages linked list for the record.

   If a full page becomes un-full after the deletion, page
   should be moved available pages linked list. If a page
   becomes empty it should removed from the file.

   The method throws a MissingResourceException if the filename is not
   found in the directory or the record is not found in the file.

   It throws an IllegalArgumentException if the record contains
   more bytes then the record size of the given heap file.

*/

To create the class LRUBufferManager, I expect you to override the two methods getPage and releasePage of the BufferManager class so that they implement an LRU page replacement policy.

To get some idea how the code below works, it is instructive to read the comments in it. It is also useful to compile the code and run it. To do this cut and paste the three classes into different files named after their respective class names. Alternatively, you can download the code using the link Hw3.zip. Then compile the code using: javac MockDisk.java and run it using java MockDisk. The program as it stands should do some simple operations on the MockDisk and print out the current contents of the drive together with some data associated with the drive. You can make similar kinds of tests to make sure your code works by overriding main in your subclass.

(Bonus 2pts) Write a robust set of tests for your code using JUnit. Submit these in the file Test.zip along with a readme for how to build and use your tests. Note if you're uninterested in the bonus, then just submit a blank file for this file.

//MockDisk.java

import java.nio.*;

/**
   This class is used to emulate an imaginary disk drive. The drive consists
   of 128 pages each of 6 bytes in length.

   Page 0 is always used for the start of the directory of heap
   files stored in the drive. Pages in the directory are stored
   in Header Page format. @see Page

   The directory consists of a linked list of header pages for different
   heap files stored in the drive. The last page of the directory is marked
   by the fact that its next link is null (i.e., 0).

   Header pages in the directory are considered to be deleted files
      if there full pages list link is the null pointer.
      To mark a page as deleted one should of course also delete
      all of the pages associated with it (but not the header page itself)
      in the PAM. To create a heap file with no data pages
      the full pages list link should be set to EMPTY_PAGE (128).

   Pages 123 - 127 are used to contain a Page Allocation Map
   (PAM). Data bytes in these pages are used to indicate which
   pages on the drive have been allocated. The four data bytes
   of page 123 indicate the allocation of the first 32 blocks of
   the drive; page 124 the next 32 blocks, etc.
   @see Page

   @author Chris Pollett
   @version 2003/10/6

*/
public class MockDisk
{
   /**
      default constructor. Allocates memory for pages in disk drive.
      creates a buffer manager and initializes it. lastly formats the drive.
    */
   public MockDisk()
   {
      Page[] drive = new Page[LAST_DISK_PAGE + 1];
      for(int i=0; i<=LAST_DISK_PAGE; i++)
      {
         drive[i] = new Page();
      }
      driveBuffer = new BufferManager();
      driveBuffer.create(BUFFER_SIZE, drive);
      format();
   }

   /**
      does the same as the default contructor but allows one
      to specify a BufferManager to use. Useful if want to
      subclass BufferManager to do a customized page replacement policy.

      @param buffer - buffer manager to use in creating this instance
          of the drive
    */
   public MockDisk(BufferManager buffer)
   {
      Page[] drive = new Page[LAST_DISK_PAGE + 1];
      for(int i=0; i<=LAST_DISK_PAGE; i++)
      {
         drive[i] = new Page();
      }
      driveBuffer = buffer;
      driveBuffer.create(BUFFER_SIZE, drive);
      format();
   }

   /**
      clears all pages in the drive. Creates the PAM and directory pages
      and allocates these pages in the PAM.

    */
   public void format()
   {
      Page p;
      byte zero = 0;

      // erase all the pages

      for(int i = 0; i <= LAST_DISK_PAGE ; i++)
      {
         p = driveBuffer.readPage(i);

         for(int j = 0; j < PAGE_SIZE; j++)
         {
            p.setByte(j , zero);
         }
         driveBuffer.setDirty(i);
         driveBuffer.releasePage(i);
      }

      int previousPage = NULL_PAGE;
      int nextPage;


      //set next and previous page links in PAM

      for(int i = PAM_START; i <= PAM_END; i++)
      {
         p = driveBuffer.readPage(i);
         p.setPreviousPage(previousPage);
         nextPage = (i < LAST_DISK_PAGE) ? i + 1 : NULL_PAGE;
         p.setNextPage(nextPage);
         previousPage = i;
         driveBuffer.setDirty(i);
         driveBuffer.releasePage(i);

      }

      //allocate pages in PAM and for start of directory

      for(int i = PAM_START; i <= PAM_END; i++)
      {
         allocatePage(i);
      }

      allocatePage(DIR_START);
   }

   /**
      sets the page allocation bit of the given page in the PAM
      to true. i.e., allocated

      @param pageNumber - number of page to allocate
   */
   public void allocatePage(int pageNumber)
   {
      setPageAllocation(pageNumber, true);
   }

   /**
      sets the page allocation bit of the given page in the PAM
      to false. i.e., not allocated

      @param pageNumber - number of page to be deallocated
    */
   public void deallocatePage(int pageNumber)
   {
      setPageAllocation(pageNumber, false);
   }

   /**
      sets the page allocation bit of the given page in the PAM
      to the supplied value

      @param pageNumber - number of page to be set
      @param allocate - boolean of whether to allocate (true) or
         deallocate (false)
    */
   public void setPageAllocation(int pageNumber, boolean allocate)
   {
      isValidNumber(pageNumber, 0, LAST_DISK_PAGE);

      int pagesPerPAMPage = DATA_SIZE << 3; // DATA_SIZE * 8
      int whichByte = DATA_START + ((pageNumber >> 3) % DATA_SIZE);

      int currentIndex = PAM_START;
      Page currentPage = driveBuffer.readPage(currentIndex);

      for(int i = PAM_START; i < PAM_START + pageNumber/pagesPerPAMPage; i++)
      {
         driveBuffer.releasePage(currentIndex);
         currentIndex = currentPage.getNextPage();
         currentPage = driveBuffer.readPage(currentIndex);
      }


      int whichBit = pageNumber & 7;

      if(allocate)
      {
         currentPage.setByte(whichByte, (byte)(currentPage.getByte(whichByte)
            | (1 << whichBit)));
      }
      else
      {
         currentPage.setByte(whichByte, (byte)(currentPage.getByte(whichByte)
            & ~(1 << whichBit)));
      }

      driveBuffer.setDirty(currentIndex);
      driveBuffer.releasePage(currentIndex);
   }


   /**
      Used to create an empty heap file and add it to the drive directory

      <b> Warning does not check if file already exists!!</b>

      @param name - two byte name of heap file
      @param recordSize
   */
   public void createFile(String name, int recordSize)
      throws BufferOverflowException, IndexOutOfBoundsException
   {
      int length = name.length();

      isValidNumber(length, 0, DATA_SIZE-2);
         //filenames at most DATA_SIZE-1 chars

      isValidNumber(recordSize, 1, DATA_SIZE);

      Page p;
      int j;
      int i;

      boolean notAddedName = true;
      int currentDirIndex;
      int next = DIR_START;

      while(notAddedName)
      {
         currentDirIndex = next;
         p = driveBuffer.readPage(currentDirIndex);
         if(isNullPage(p.getByte(HEADER_FULL_INDEX)))
            /* checks if header page not in
               use: either just allocated or a deleted file.
               If yes then use it to create file.
               If page in use then this byte will either point to
               a list of full data pages in the given heap file
               or will point to EMPTY_PAGE, if the full page list is
               empty.
            */
         {
            notAddedName = false;
            for(i = DATA_START, j=0; i < DATA_START + length; i++, j++)
            {

               p.setByte(i, (byte)name.charAt(j) );
            }
            driveBuffer.setDirty(currentDirIndex);
            p.setByte(HEADER_FULL_INDEX, (byte)EMPTY_PAGE);
            p.setByte(HEADER_AVAILABLE_INDEX, (byte)DIR_START);
               //indicates the size of records in this file

            p.setRecordSize(recordSize);
         }

         next = p.getNextPage();

         // check if need to extend directory for one more file
         if(notAddedName && isNullPage(next))
         {

            appendPage(currentDirIndex, firstFreePage());
            next = p.getNextPage();
         }

         driveBuffer.releasePage(currentDirIndex);
      }

   }

   /**
      Computes the first not allocated page listed in the PAM

      return - page number of a free page
    */
   public int firstFreePage() throws BufferOverflowException
   {
      boolean notFound = true;

      Page p;
      int currentIndex = PAM_START;
      int j;
      int data = 0;
      int cnt = 0;

      while( notFound && cnt <= LAST_DISK_PAGE)
      {
         p=driveBuffer.readPage(currentIndex);

         j = DATA_START-1;

         while(j < PAGE_SIZE - 1 && notFound)
         {
            j++;
            data = p.getByte(j) &255;
            if(data < 255) notFound=false;
            cnt += 8;

         }
         driveBuffer.releasePage(currentIndex);
         currentIndex++;
      }

      if(notFound) throw new BufferOverflowException();
      cnt -= 8;
      j=0;

      while((data | (1<<j)) == data)
      {
         j++;
         cnt++;
      }

      return cnt;

   }



   /**
      Creates a string with details of the current drive state

      @return - string with details
    */
   public String toStringDrive()
   {
      String out = "";
      out += "Drive's directory:\n";
      out += toStringDirectory();

      out += "Page Allocation Map (PAM) \n";
      out += "Each row gives current allocation of eight pages.\n";
      out += "First row starts with Page 0.\n";

      out += toStringPAM();

      out +="\nData View of the contents of each page:\n";

      out += toStringPages(0, LAST_DISK_PAGE);

      return out;
   }

   /**
      Creates a string with details of the heap files stored
      in the drives directory

      @return - string with details
    */
   public String toStringDirectory()
   {
      boolean notEnd =true;
      int currentDirIndex = DIR_START;
      int next;
      int i = 0;
      Page p;
      String out ="";

      while( notEnd )
      {
         p = driveBuffer.readPage(currentDirIndex);
         next = p.getNextPage();
         notEnd = !isNullPage(next);

         out += "File " + i + ": " + p.toStringHeaderPage() + "\n";

         driveBuffer.releasePage(currentDirIndex);
         currentDirIndex = next;
         i++;
      }
      return out;
   }

   /**
      Creates a string with details of which blocks
      the PAM thinks are allocated and which not

      @return - string with details
    */
   public String toStringPAM()
   {
      Page p;
      int j = 0;
      int currentPAMPage = PAM_START;
      int bytesLeft = PAM_SIZE;
      int nextPage;
      int tmp;
      String out="";

      while(!isNullPage(currentPAMPage))
      {
         p = driveBuffer.readPage(currentPAMPage);

         tmp = (bytesLeft > DATA_SIZE) ? DATA_SIZE : bytesLeft;

         for(int i = DATA_START; i <  DATA_START+tmp; i++)
         {
            out += p.toStringByte(i) + "\n";
         }

         nextPage = p.getNextPage();
         driveBuffer.releasePage(currentPAMPage);

         currentPAMPage = nextPage;
         bytesLeft -= DATA_SIZE;
      }
      return out;
   }

   /**
      Creates a string which lists out the contents of
      a sequence of data pages from the drive

      @return - the string with contents
    */
   public String toStringPages(int low, int high)
   {
      Page p;
      String out = "";

      for(int i = low; i <= high; i++)
      {
         out += "Page " + i + ": ";

         p = driveBuffer.readPage(i);
         out += p.toStringDataPage() +"\n";

         driveBuffer.releasePage(i);
      }

      return out;
   }

   /* END PUBLIC METHODS */

   /**
      Appends the page next to the page node
      in a heap file. It is assumed that
      page next should have no data (newly allocated).
      So its record count is set to zero. Further
      next's next link is set to null

      @param node - page that will append to
      @param next - page that is being appended
    */
   void appendPage(int node, int next)
   {
      isValidNumber(node, 0, LAST_DISK_PAGE);
      isValidNumber(next, 0, LAST_DISK_PAGE);

      allocatePage(next);

      Page p = driveBuffer.readPage(node);
      p.setNextPage(next);
      driveBuffer.setDirty(node);
      driveBuffer.releasePage(node);

      p = driveBuffer.readPage(next);
      p.setPreviousPage(node);
      p.setNextPage(NULL_PAGE);
      p.setNumberOfRecords(0);
      driveBuffer.setDirty(next);
      driveBuffer.releasePage(next);
   }

   /**
      Checks if the given pageNumber is a null page.
      0 is the null page.

      @param pageNumber - page to check if null pointer
      @return - whether or not was zero page
    */
   boolean isNullPage(int pageNumber)
   {
      return (pageNumber == NULL_PAGE);
   }

   /**
    Used with header pages @see Page of heap files. Is used to
    check that the give heap file has an empty full pages list
    but the file is not marked for deletion.

    @param pageNumber - page index to check
    @return - whether page number is the EMPTY_PAGE
    */
   boolean isEmptyPage(int pageNumber)
   {
      return (pageNumber == EMPTY_PAGE);
   }

   /**
      Checks if number is <=high and >= low. If not throws an
      IndexOutofBoundsException. Method says which class it belongs
      to (maybe could have avoided code duplication)

      @param number - number to check
      @param low - low value of range number should be in
      @param high - high value of range number should be in
      @throws IndexOutOfBoundsException - returned if number not in range
    */
   static void isValidNumber(int number, int low, int high)
      throws IndexOutOfBoundsException
   {
      if(number > high || number < low)
         throw new IndexOutOfBoundsException(
            "(MockDisk Index Exception) Number was:" +
             number + ".\n Maximum allowable is: " + high +
            ".\n Minimum allowable is: " + low + ".\n");
   }

   /*
      Some simple tests to illustrate how the drive works.
   */
   public static void main(String args[])
   {
      MockDisk m = new MockDisk();
      m.createFile("hi",1);
      m.createFile("yo",2);
      m.createFile("ho",3);
      m.createFile("h2",4);
      System.out.println(m.toStringDrive());

      for(int i=0; i < BUFFER_SIZE; i++)
      {
        System.out.println(m.driveBuffer.toStringBufferPoolPage(i));
      }
   }

   public final static int LAST_DISK_PAGE = Page.MAX_PAGE_INDEX;
      //index of last page stored in drive

   public final static int BUFFER_SIZE = 4;
      // number of pages in the buffer pool the buffer manager has

   public final static int PAGE_SIZE = Page.PAGE_SIZE;
      // size of a disk page in bytes

   public final static int DATA_SIZE = Page.DATA_SIZE;
      // number of bytes of data that can be stored in a disk page

   public final static int DATA_START = PAGE_SIZE - DATA_SIZE ;
      // byte offset to first data byte in a page.

   public final static int PAM_SIZE = (LAST_DISK_PAGE+1)/8;
         //Number of bytes of data needed for PAM

   public final static int PAM_START = LAST_DISK_PAGE-(PAM_SIZE+1)/DATA_SIZE;
         //first page of PAM

   public final static int PAM_END = LAST_DISK_PAGE;
         //Size of PAM in bytes

   public final static int HEADER_FULL_INDEX = PAGE_SIZE-1;
      // index of the byte used in a header page to store a link to the list
      // of full pages in a heap file

   public final static int HEADER_AVAILABLE_INDEX = PAGE_SIZE-2;
   // index of the byte used in a header page to store a link to the list
   // of avilable pages (those that can store more records) in a heap file

   public final static int EMPTY_PAGE = 128;
   // used ot indicate a heap files full page list is empty
   // but the file is not deleted.

   public static int DIR_START = 0;
         //Location of directory in drive

   public static int NULL_PAGE = 0;
         //Next/prev page pointing into Directory
         //indicates no next/prev page.

   BufferManager driveBuffer;
         // buffer used to manage disk I/Os

}

//BufferManager.java

import java.nio.*;
import java.util.*;
/**
    This class is used to manage reading and writing pages on a
    MockDisk object. All read requests from a MockDisk should go
    through the drive's BufferManager object: driveBuffer.
    To get a reference to the page numbered i, one calls

    Page p = driveBuffer.readPage(i);

    This pins the page in the BufferManager's buffer pool. So
    when one is done with the page one has to call

    driveBuffer.releasePage(i);

    If one has a reference to a page via the buffer and one wants
    to change the page then it is the user of the pages responsibility
    to set the dirty bit of the page so that the BufferManager knows
    to write the page back to disk if it gets replaced from the buffer.
    To set the dirty bit one does:

    driveBuffer.setDirty(i);

    BEFORE releasing the page.

    @author Chris Pollett
    @version 2003/10/6
*/

public class BufferManager
{
   /**
         Empty constructor. Set-up of class deferred to create method below
   */
   public BufferManager()
   {
   }

   /**
      Sets up the BufferManager creates the bufferPool,
      and arrays to keep track pin count, dirtiness and which
      pages are in the pool. Sets the passed drive full of free
      pages to be the pages managed by this BufferManager.

      @param size - how many pages the buffer pool will be
      @param drive - pages used to represent the disk drive
    */
   public void create(int size, Page[] drive)
   {
      bufferSize = size;
      bufferPool = new Page[size];
      pinCount = new int[size];
      pageNumbers = new int[size];
      dirty = new boolean[size];
      this.drive=drive;

      storedPages= new Hashtable();

      driveSize = drive.length;
      clock = 0;

      for(int i = 0; i < size; i++)
      {
         bufferPool[i] = new Page();
      }
   }

   /**
      Tries to find requested page in buffer pool and if so
      updates the pin count of the page and returns a
      reference to it. If not it uses getPage to swap the page
      into the buffer pool.

      @param pageNumber - which page of drive to return

      @throws IndexOutOfBoundsException - thrown if pageNumber not in drive

      @return - requested page
   */
   public Page readPage(int pageNumber) throws IndexOutOfBoundsException
   {
      isValidNumber(pageNumber, 0, driveSize);
      Object indexObj=storedPages.get(new Integer(pageNumber));
      int index;
      if(indexObj == null)
      {
         index = getPage(pageNumber);
         pinCount[index] = 1;
      }
      else
      {
         index = ((Integer)indexObj).intValue();
         pinCount[index]++;
      }

      return bufferPool[index];
   }
   /**
      Sets the dirty bit of the indicated page. The dirty bit being
      set indicates that if the page is removed from the buffer
      it must be first written back to disk.

      @param pageNumber - page whose dirty bit needs to be set

      @throws MissingResourceException - thrown if page not currently in buffer
    */
   public void setDirty(int pageNumber) throws MissingResourceException
   {
      isValidNumber(pageNumber, 0, driveSize);
      Object indexObj=storedPages.get(new Integer(pageNumber));
      if(indexObj == null)
         throw new MissingResourceException("Page not in buffer pool.",
            "BufferManager", ""+pageNumber);

      dirty[((Integer)indexObj).intValue()] = true;

   }


   /**
      Releases one pin on the given page in the buffer pool.

      @param pageNumber - page to release a pin of.
      @throws IllegalStateException - if pin count on a page goes negative.
    */
   public void releasePage(int pageNumber)
      throws IllegalStateException

   {
      isValidNumber(pageNumber, 0, driveSize);

      Object indexObj = storedPages.get(new Integer(pageNumber));
      if(indexObj == null)
         throw new MissingResourceException("Page not in buffer pool.",
            "BufferManager", ""+pageNumber);

      int index =((Integer)indexObj).intValue();
      if(pinCount[index] <= 0)
         throw new IllegalStateException("Page "+pageNumber
            + "released too many times");

      pinCount[index]--;


   }

   /**
      Gets a page that is not currently in the buffer pool.
      If the page that is replaced has its dirty bit
      set then it is written back to the disk. This
      method uses the clock replacement policy to determine
      which should be the next buffer page to be used
      to read disk page into. For the Homework you should
      override this method to use the LRU policy.

      @param pageNumber - page to retrieve.

      @throws BufferOverflowException - if page is not in buffer and
          all buffer pages are currently pinned.
    */
   int getPage(int pageNumber)
      throws BufferOverflowException
   {
      isValidNumber(pageNumber, 0, driveSize);
      int index = clock + 1;
      index = (index < bufferSize) ? index : 0;
      while( index != clock && pinCount[index] > 0)
      {
         index++;
         index = (index < bufferSize) ? index : 0;
      }

      if( pinCount[index] > 0)
         throw new BufferOverflowException();

      if(dirty[index])
      {
         drive[pageNumbers[index]].copy(bufferPool[index]);
         dirty[index] = false;
      }

      storedPages.remove(new Integer(pageNumbers[index]));
      bufferPool[index].copy(drive[pageNumber]);
      pageNumbers[index] = pageNumber;
      storedPages.put(new Integer(pageNumber), new Integer(index));
      pinCount[index] = 1;

      clock++;
      clock = (clock < bufferSize) ? clock : 0;

      return index;
   }

   /**
      Generates a String to represent the data stored about a given page
      in the buffer pool. i.e., which disk page, its pin count,
      its dirtyness and its data.

      @return the string with this information
   */
   public String toStringBufferPoolPage(int i)
   {
      isValidNumber(i, 0, bufferSize-1);
      String out = "";
      out += "Buffer Page: " + i + " pageNumber: "+ pageNumbers[i];
      out += " pinCount: "+ pinCount[i] + " dirty: " + dirty[i] +"\n";

      out += "\t"+bufferPool[i].toStringDataPage();
      return out;

   }

   /**
      Checks if number is <=high and >= low. If not throws an
      IndexOutofBoundsException. Method says which class it belongs
      to (maybe could have avoided code duplication)

      @param number - number to check
      @param low - low value of range number should be in
      @param high - high value of range number should be in
      @throws IndexOutOfBoundsException - returned if number not in range
    */
   static void isValidNumber(int number, int low, int high)
      throws IndexOutOfBoundsException
   {
      if(number > high || number < low)
         throw new IndexOutOfBoundsException("(Buffer Index Exception)" +
            "Number was:" + number +".\n Maximum allowable is: " + high +
            ".\n Minimum allowable is: " + low + ".\n");
   }

   int driveSize; // size in number of pages in the drive

   int bufferSize; // size in number of pages in the buffer pool

   int clock; /*keeps track of where the clock is at in terms of cycling
                through pages in the buffer pool for clock replacement
                buffer policy */

   Page[] bufferPool; // pages used for the buffer

   Page[] drive; // the actual pages in the disk drive

   int[] pinCount; // used to keep track of the number of times a
                   // page has been pinned

   int[] pageNumbers; // used to keep track of which page a given
                      // buffer stores

   boolean[] dirty; // used to keep track of which buffer pages are dirty

   Hashtable storedPages; // used to quickly look-up via drive page number
                          // where a give page is stored in the buffer pool
}

//Page.java

/**
    This class is used to represent one page of our MockDisk disk drive

    There are two kinds of pages used in our mock disk drive, both of
    which use instances of this class to store: data pages and header pages.
    For more robust purposes might have used subclassing.

    In a data page the bytes are arranged as

      byte#  Use
      0      low order seven bits - next page link
             high order bit is high order bit of record count
      1      low order seven bits - previous page link
             high order bit is low order bit of record count
      2-5    data bytes

      As mentioned above the two high order bits combine together
      to form a two bit number indicating how many records are stored
      in this data page.

      Data pages are used to store the data in a heap file as well as
      being used in the PAM.

    In a header page the bytes are arranged as

      byte#  Use
      0      low order seven bits - next page link
             high order bit is high order bit of record count
      1      low order seven bits - previous page link
             high order bit is low order bit of record count
      2-3    file name
      4      pageNumber of the list of pages with available space
             in this heap file
      5      pageNumber of the list of pages which are full
             in this heap file

      As mentioned above the two high order bits combine together
      to form a two bit number indicating the record size in bytes
      used in this heap file.

      Header pages are used in the drive directory.  If byte
      5 is set to 0 (NULL_PAGE). It indicates this file is deleted.
      If byte 5 is set to 128 (EMPTY_PAGE). It indicates the
      full page list is empty but the file is still in use
      (not deleted).

    @author Chris Pollett
    @version 2003/10/6

*/

public class Page
{
   /**
      Allocates memory for the bytes of this page
   */
   public Page()
   {
      data = new byte[PAGE_SIZE];
   }

   /**
      Copy constructor for this class

      @param p - Page to be copied
   */
   public Page(Page p)
   {
      data = new byte[PAGE_SIZE];
      copy(p);
   }

   /**
      Copies the page p into the current page object

      @param - page to be copied
   */
   public void copy(Page p)
   {
      for(int i=0; i < PAGE_SIZE; i++)
      {
         data[i] = p.data[i];
      }
   }

   /**
      Gives an integer reference to the next page in the file after
      this page.

      @return - index of the next page
   */
   public int getNextPage()
   {
      return data[0] & MAX_PAGE_INDEX;
   }

   /**
      sets the next page link off this page to the indicated value

      @param pageNumber

   */
   public void setNextPage(int pageNumber)
   {
      isValidNumber(pageNumber, 0, MAX_PAGE_INDEX);

      data[0] &=MASK;
      data[0] += (byte)pageNumber;
   }

   /**
      Gives an integer reference to the previous page in the file before
      this page.

      @return - index of the next page
    */
   public int getPreviousPage()
   {
      return data[1] & MAX_PAGE_INDEX;
   }

   /**
      sets the previous page link off this page to the indicated value

      @param pageNumber
    */
   public void setPreviousPage(int pageNumber)
   {
      isValidNumber(pageNumber, 0, MAX_PAGE_INDEX);

      data[1] &=MASK;
      data[1] += (byte)pageNumber;
   }



   /**
      If the page is a data page then this returns the number
      of records stored in the data page.

      @return - number of records in this page
   */
   public int getNumberOfRecords()
   {
      return  (((data[0] >> 7)& 1) << 1) | (data[1]>>7 &1);
   }

   /**
      If the page is a header page then this returns the number
      of bytes ina record that appears in this heap file.

      @return - number of bytes in a record
    */
   public int getRecordSize()
   {
      return getNumberOfRecords() + 1;
   }

   /**
      Used in a header page of a heap file to indicate the size of
      records stored in the file.

      @param recordSize - size of the records in this heap file in bytes
    */
   public void setRecordSize(int recordSize)
   {
      --recordSize;
      setNumberOfRecords(recordSize);
   }
   /**
      Used in a data page of a heap file to set the value of the number of
      records currently stored in the page.

      @param number - what value to set it to
    */
   public void setNumberOfRecords(int number)
   {
      isValidNumber(number, 0, DATA_SIZE -1);
      data[0] &= 127;
      data[1] &= 127;
      data[0] |= (number & 2)<< 6;
      data[1] |= ( number & 1) << 7;
   }

   /**
      Returns the given index byte from the bytes stored on page

      @param byteNumber - index of byte to return

      @return - a byte from page
    */
   public byte getByte(int byteNumber)
   {
      isValidNumber(byteNumber, 0, PAGE_SIZE-1);
      return data[byteNumber];
   }

   /**
      Sets the value of the specified byte of this page

      @param byteNumber - index of byte to set
      @param value - value to set that byte to
    */
   public void setByte(int byteNumber, byte value)
   {
      isValidNumber(byteNumber, 0, PAGE_SIZE-1);
      data[byteNumber] = value;
   }


   /**
      Generates from byte i of the bytes in this page a
      String of representating the settings of the indiviual bits
      in this byte.

      @return string representation of the requested byte
    */
   public String toStringByte(int i)
   {
      isValidNumber(i, 0, PAGE_SIZE-1);
      int b = getByte(i);
      String out = "";

      for(int j = 1; j <= 128; j <<= 1)
      {
         if( (b & j) > 0)
         {
            out += " 1";
         }
         else
         {
            out += " 0";
         }
      }
      return out;
   }

   /**
      Generates a string that gives informations about the contents
      of the page assuming the page is the data page of some heap file

      @return  - string with information
    */
   public String toStringDataPage()
   {
      String out ="";
      out += "Next Page: "+ getNextPage();
      out += " Previous Page: " + getPreviousPage();
      out += " Record Cnt: " + getNumberOfRecords();
      out += " Data: [";

      for(int j = DATA_START; j < PAGE_SIZE; j++)
      {
         int b = getByte(j) & 255;
         out += " "+ b;
      }
      out += " ]";
      return out;
   }
   /**
      Generates a string that gives informations about the contents
      of the page assuming the page is the header page of some heap file

      @return  - string with information
   */
   public String toStringHeaderPage()
   {
      String out = "Name: ";

      for(int i = DATA_START ; i < PAGE_SIZE - 2; i++)
      {
         out += (char)getByte(i);
      }
      out += " Next Page: "+ getNextPage();
      out += " Previous Page: " + getPreviousPage();

      out += " Record Size: " + getRecordSize();
      out += " Available List Link:" + (getByte(PAGE_SIZE - 2) & 255);
      out += " Full List Link:" + (getByte(PAGE_SIZE - 1) & 255);

      return out;
   }

   /**
      Checks if number is <=high and >= low. If not throws an
      IndexOutofBoundsException. Method says which class it belongs
      to (maybe could have avoided code duplication)

      @param number - number to check
      @param low - low value of range number should be in
      @param high - high value of range number should be in

      @throws IndexOutOfBoundsException - returned if number not in range
    */
   static void isValidNumber(int number, int low, int high)
      throws IndexOutOfBoundsException
   {
      if(number > high || number < low)
         throw new IndexOutOfBoundsException("(Page Index Exception) Number was:"
            + number +".\n Maximum allowable is: " + high +
            ".\n Minimum allowable is: " + low + ".\n");
   }
   public static int PAGE_SIZE=6; //number of bytes in a page

   public static int DATA_SIZE=4; // number of bytes of data in a page
                                  // (ignoring next and previous links)

   public static int DATA_START = PAGE_SIZE - DATA_SIZE ;
                                 //byte offset to first byte of data

   public static int MAX_PAGE_INDEX=127;
                                 // maximum number that can occur
                                 // in a next and previous link

   public static int MASK=128; // used to select for the high order bit of a byte;

   byte[] data; // actual data stored in page

}

Point Breakdown

Departmental coding guidelines for Java followed 1pt
addRecord works 1pt
deleteRecord works1pt
deleteFile and toStringFile work as described (1/2 pt each) 1pt
Subclassing BufferManager to use LRU as described by overriding getPage and release2pts
12.5 and 13.3 (2pts each)4pts
Total10pts